package utilities;

import java.util.LinkedList;
import java.util.Stack;

import model.EdgeTransitionSystem;
import model.NodePathsHashTable;
import model.NodeTransitionSystem;
import model.TransitionSystem;

/** Class for simulation of the algorithm for supervisor creator for deterministic/non-deterministic graph without variables
 * @author DarkoLaptop2
 *
 */
public class NVSupervisorCreator {
	
	/** Method for creation of a supervisor for a given deterministic graph without variables
	 * @param graph - the deterministic graph without variables
	 * @return  - a graph representing the supervisor for this graph
	 */
	public static TransitionSystem createFromDeterministic(TransitionSystem graph){
		System.out.println("Deterministric");
		System.out.println(graph.toStringTransitionPrint());
		System.out.println(graph.getAllUncontrollableTransitions().length);
		for(String transition:graph.getAllUncontrollableTransitions())
			System.out.println(transition);
		if(graph.getInitialNode().isBadNode())
			return null;
		
		Stack<NodeTransitionSystem> stack=new Stack<NodeTransitionSystem>();
		for(NodeTransitionSystem badNode:graph.getAllBadNodes())
			stack.push(badNode);
		
		while(!stack.isEmpty()){
			NodeTransitionSystem currentNode=stack.pop();
			
			LinkedList<EdgeTransitionSystem> parentTransitions=graph.getNodeParents(currentNode.getIndex());
			for(EdgeTransitionSystem transition:parentTransitions){
				if(graph.isControllableTransition(transition.getTransitionName()))
				{
					//graph.removeTransition(transition);
				}
				else {
					if(!transition.getFromNode().isBadNode()){
						transition.getFromNode().setBadNode(true);
						stack.push(transition.getFromNode());
					}
				}
			}
		}
		
		if(graph.getInitialNode().isBadNode())
			return null;
		
		TransitionSystem supervisor=new TransitionSystem();
		LinkedList<NodeTransitionSystem> queue=new LinkedList<NodeTransitionSystem>();
		queue.addLast(graph.getInitialNode());
		supervisor.addNode(graph.getInitialNode().getIndex(), graph.getInitialNode().getName());
		supervisor.setInitialNode(graph.getInitialNode().getIndex());
		while(!queue.isEmpty()){
			NodeTransitionSystem currentNode=queue.removeFirst();
			for(EdgeTransitionSystem currentNeighbour:currentNode.getNeighbors()){
				if(!currentNeighbour.getToNode().isBadNode()){
					if(!supervisor.isSetNode(currentNeighbour.getToNode().getIndex())){
						supervisor.addNode(currentNeighbour.getToNode().getIndex(), currentNeighbour.getToNode().getName());
						queue.addFirst(currentNeighbour.getToNode());
					}
					supervisor.addEdge(currentNeighbour.getFromNode().getIndex(), currentNeighbour.getTransitionName(), currentNeighbour.getToNode().getIndex());
				}
			}
		}
		
		return supervisor;
		
	}
	
	/** Method for creation of a supervisor for a given nondeterministic graph without variables
	 * @param graph - the nondeterministic graph without variables
	 * @return  - a graph representing the supervisor for this graph
	 */
	public static TransitionSystem createFromNondeterministic(TransitionSystem graph){
		System.out.println("Non deterministic");
		if(graph.getInitialNode().isBadNode())
			return null;
		
		NodePathsHashTable hashTable=graph.generateAllPaths();
		Stack<NodeTransitionSystem> stack=new Stack<NodeTransitionSystem>();
		for(NodeTransitionSystem badNode:graph.getAllBadNodes()){
			stack.push(badNode);
			for(String path:hashTable.getNodePaths(badNode.getIndex()))
				for(NodeTransitionSystem node:graph.getAllNodesWithSameTrace(path))
					if(!node.isBadNode()){
						stack.push(node);
						node.setBadNode(true);
					}
		}
		while(!stack.isEmpty()){
			NodeTransitionSystem currentNode=stack.pop();
			LinkedList<EdgeTransitionSystem> parentTransitions=graph.getNodeParents(currentNode.getIndex());
			for(EdgeTransitionSystem transition:parentTransitions){
				if(graph.isControllableTransition(transition.getTransitionName()))
				{
//					//graph.removeTransition(transition);
				}
				else {
					if(transition.getFromNode().getIndex()==graph.getInitialNode().getIndex())
						return null;
					for(String trace:hashTable.getNodePaths(transition.getFromNode().getIndex())){
						for(NodeTransitionSystem node:graph.getAllNodesWithSameTrace(trace)){
							if(!node.isBadNode()){
								node.setBadNode(true);
								stack.push(node);
							}
						}
					}
					
				}
			}
		}
		
		if(graph.getInitialNode().isBadNode())
			return null;
		
		TransitionSystem supervisor=new TransitionSystem();
		LinkedList<NodeTransitionSystem> queue=new LinkedList<NodeTransitionSystem>();
		queue.addLast(graph.getInitialNode());
		supervisor.addNode(graph.getInitialNode().getIndex(), graph.getInitialNode().getName());
		supervisor.setInitialNode(graph.getInitialNode().getIndex());
		while(!queue.isEmpty()){
			NodeTransitionSystem currentNode=queue.removeFirst();
			for(EdgeTransitionSystem currentNeighbour:currentNode.getNeighbors()){
				if(!currentNeighbour.getToNode().isBadNode()){
					if(!supervisor.isSetNode(currentNeighbour.getToNode().getIndex())){
						supervisor.addNode(currentNeighbour.getToNode().getIndex(), currentNeighbour.getToNode().getName());
						queue.addFirst(currentNeighbour.getToNode());
					}
					supervisor.addEdge(currentNeighbour.getFromNode().getIndex(), currentNeighbour.getTransitionName(), currentNeighbour.getToNode().getIndex());
				}
			}
		}
		
		return supervisor;
		
	}
}
